package cs.upt.awp.controller;

import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.hibernate.exception.ConstraintViolationException;
import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import cs.upt.awp.data.User;
import cs.upt.awp.data.UserRole;
import cs.upt.awp.services.TravelUseRolesServices;
import cs.upt.awp.services.TravelUsersServices;


@Controller
public class AccessController {
	
	Logger logger = Logger.getLogger(AccessController.class);
	
	@Autowired
	TravelUsersServices usersServices;

	@Autowired
	TravelUseRolesServices userRolesServices;
	
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public ModelAndView login(
			@RequestParam(value = "error", required = false) String error,
			@RequestParam(value = "logout", required = false) String logout,
			HttpServletRequest request) {

		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error",
					getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
		}

		if (logout != null) {
			model.addObject("msg", "You've been logged out successfully.");
		}
		model.setViewName("login");

		return model;

	}
	
	@RequestMapping(value = "/welcome", method = RequestMethod.GET)
	public ModelAndView welcomePage(HttpServletRequest request) {

		ModelAndView model = new ModelAndView();
		Set<String> roles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
        if (roles.contains("ROLE_ADMIN")) {
        	model.setViewName("admin");
            return model;
        }
        model.addObject("username",request.getRemoteUser().toString());
        model.setViewName("welcome");
		return model;

	}

	// customize the error message
	private String getErrorMessage(HttpServletRequest request, String key) {

		Exception exception = (Exception) request.getSession()
				.getAttribute(key);

		String error = "";
		if (exception instanceof BadCredentialsException) {
			error = "Invalid username and password!";
		} else if (exception instanceof LockedException) {
			error = exception.getMessage();
		} else {
			error = "Invalid username and password!";
		}

		return error;
	}

	// for 403 access denied page
	@RequestMapping(value = "/403", method = RequestMethod.GET)
	public ModelAndView accesssDenied() {

		ModelAndView model = new ModelAndView();

		// check if user is login
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		if (!(auth instanceof AnonymousAuthenticationToken)) {
			UserDetails userDetail = (UserDetails) auth.getPrincipal();
			System.out.println(userDetail);

			model.addObject("username", userDetail.getUsername());

		}

		model.setViewName("403");
		return model;

	}
	
	@RequestMapping(value = "/admin**", method = RequestMethod.GET)
	public ModelAndView adminPage() {

		ModelAndView model = new ModelAndView();
		model.addObject("title", "Spring Security + Hibernate Example");
		model.addObject("message", "This page is for ROLE_ADMIN only!");
		model.setViewName("admin");

		return model;

	}
	
	@RequestMapping(value = "/register", method = RequestMethod.GET)
	public ModelAndView registrationPage(){
		ModelAndView model = new ModelAndView();
		model.addObject("user", new User());
		model.setViewName("register");
		return model;
	}
	
	@RequestMapping(value = "/register", method = RequestMethod.POST)
	public ModelAndView registrationSubmit(@ModelAttribute("user") User user){
		ModelAndView model = new ModelAndView();
		
		if((user.getUserName() == null || "".equals(user.getUserName())) && (user.getPassword() == null || "".equals(user.getPassword()))){
			model.addObject("error", "Username & Password cannot be empty!!");				
			model.setViewName("register");
			return model;
		}else if(user.getUserName() == null || "".equals(user.getUserName())){
			model.addObject("error", "Username cannot be empty!!");				
			model.setViewName("register");
			return model;
		}else if(user.getPassword() == null || "".equals(user.getPassword())){
			model.addObject("error", "Password cannot be empty!!");				
			model.setViewName("register");
			return model;
		}
		
		Set<UserRole> roles = new HashSet<UserRole>();
		UserRole role = new UserRole(user, "ROLE_USER");
		roles.add(role);
		ShaPasswordEncoder encoder = new ShaPasswordEncoder();
		System.out.println("user: "+user);
		try {
			user.setPassword(encoder.encodePassword(user.getPassword(), null));
			user.setUserRole(roles);
			usersServices.addEntity(user);
			userRolesServices.addEntity(role);
			model.addObject("username", user.getUserName());
			model.setViewName("account");
		} catch (ConstraintViolationException e) {			
				model.addObject("error", "User already exist!!");				
				model.setViewName("register");
				System.out.println("model: " + model.getModel());
		}	catch (Exception e) {			
				model.addObject("error", "Could not create the account!");				
				model.setViewName("register");
				System.out.println("model: " + model.getModel() + "error: "+e);			
		}		
		return model;
	}


}
